package impl.agentt.pEns;

import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.logging.Logger;

import apps.BehTB;

import reservation.PlageHoraire;
import contraintes.ContrainteDispo;
import contraintes.Seance;
import debug.message.PEnsDebugText;
import fr.irit.smac.may.lib.components.messaging.receiver.AgentRef;
import fr.irit.smac.may.lib.interfaces.Push;
import fr.irit.smac.may.lib.pmbehaviour.PatternMatchingBehavior;
import message.EDTMessage;
import message.PresencePlacee;
import message.enseignant.PlacementImpossible;
import message.presence.DonneCritP;
import message.presence.ValiderInscriptionP;
import message.presence.ValiderInscriptionPSansPh;
import mysticedt.BehaviourP;

public class BehaviourPEns extends BehaviourP {
	/**
	 * Reference vers l'agent Enseignant
	 */
	private AgentRef createur;
	/**
	 * Liste des disponibilite de l'enseignant.
	 */
	private ArrayList<ContrainteDispo> listContraintesDispos;
	/**
	 * Seance qui doit etre place
	 */
	private Seance s;
	/**
	 * Plage Horaire dans laquelle la seance a ete reserve
	 */
	private PlageHoraire maPlageHoraire = null;
	/**
	 * Table des agents pEtu inscrits pour ce cours, avec leur criticite.
	 */
	private Hashtable<AgentRef, Integer> listInscrits = new Hashtable<AgentRef, Integer>();
	/**
	 * Criticite de l'agent pEns.
	 */
	private Integer criticite = 0;
	
	PatternMatchingBehavior pmb = new PatternMatchingBehavior(this);
	
	public void defaultCase(Object message) {
		Logger log = Logger.getLogger("alerte");
		log.info("Alerte par "+this.getClass().getCanonicalName()+" : Incomprehension du message "+message.getClass().getCanonicalName());
	}
	
	/**
	 * Reception d'une demande d'initialisation 
	 * - enregistrement des informations
	 * - lancement du mode recherche
	 */
	public void caseInitPresenceEnsAgent(Seance se, AgentRef c, ArrayList<ContrainteDispo> l) {
		listContraintesDispos = l;
		s = se;
		createur = c;
		debugText("Initialisation : OK - Createur : %s - Seance : %s",createur.getName(),s.getId());
		
		recherche();
	}

	/**
	 * Comportement de base d'un agent pEns
	 */
	private void recherche() {
		debugText("Passage en mode recherche...");
		Boolean valide = false;
		Iterator<ContrainteDispo> iteCDisp = listContraintesDispos.iterator();
		while(iteCDisp.hasNext()) {
			ContrainteDispo current = iteCDisp.next();
			Date temp = current.getDateDebut();
			do {
				debugText("Je demande pour le %s/%s/%s a partir de %s h",temp.getDate(),temp.getMonth(),temp.getYear(),temp.getHours());
				maPlageHoraire = plageHoraireBooking().demanderCreneau(temp, s.getDuree());
				debugText("On me propose : %s",maPlageHoraire.toString());
				if(BehTB.plageHoraireIsInContrainteDispo(maPlageHoraire, current)) {
					debugText("Demande confirmation a %s",createur.getName());
					send().send(new PresencePlacee(myRef().pull(),maPlageHoraire), createur);
					valide = true;
					break;
				}
				temp = maPlageHoraire.getDateFin();
			} while(temp.before(current.getDateFin()) && !valide);
		}
		if(!valide) {
			maPlageHoraire = null;
			debugText("Placement : Impossible");
			send().send(new PlacementImpossible(myRef().pull(), listContraintesDispos), createur);
		}
	}

	/**
	 * Methode appelee lorsqu'un agent recoit un message de type DemandeCritP.
	 * 
	 * @param a
	 */
	// Un agent demande notre criticite
	public void caseDemandeCritP(AgentRef a) {
		send().send(new DonneCritP(myRef().pull(), criticite), a);
		// TODO retour au comportement nominal
	}

	/**
	 * Methode appelee lorsqu'un agent recoit un message de type DonneCritP.
	 * 
	 * @param a
	 */
	// Une presence noud donne sa criticite
	public void caseDonneCritP(AgentRef a, Integer i) {
		// TODO traiter l'info (si on a demande sa criticite a une autre
		// presence c'est surement pas pour rien
	}
	
	/**
	 * Methode appelee lorsqu'un agent recoit un message de type DemandeInscriptionP.
	 * un etudiant veut s'incrire au cours, il donne sa criticite
	 * @param a
	 * @param i
	 */
	public void caseDemandeInscriptionP(AgentRef a, Integer i) {
		debugText("Reception demande inscription %s",a.getName());
		if(maPlageHoraire==null) {
			debugText("Inscription de %s : OK",a.getName());
			listInscrits.put(a, i);
			debugText("Envoie la validation de l'inscription a %s",a.getName());
			send().send(new ValiderInscriptionPSansPh(myRef().pull()), a);
		} else {
			if (s.getCapacite()>listInscrits.size()) {
				debugText("Inscription de %s : OK",a.getName());
				listInscrits.put(a, i);
				debugText("Envoie la validation de l'inscription a %s",a.getName());
				send().send(new ValiderInscriptionP(myRef().pull(),maPlageHoraire), a);
			} else {
				debugText("Inscription de %s : Impossable",a.getName());
			}
		}
	}
	
	public void caseAcceptePlacement() {
		debugText("Placement : OK");
		criticite = 0;
	}
	
	public void caseRefusPlacement(ArrayList<ContrainteDispo> listCDisp) {
		debugText("Placement : Refuse");
		listContraintesDispos = listCDisp;
		recherche();
	}

	/*
	 * TODO Demande d'une autre pEns de liberer la plageHorraire (la pEns est
	 * plus critique que nous) SNC 1
	 */
	public void caseDemandeLiberation(AgentRef a) {
		maPlageHoraire = null;
		// on previent tous ses inscrit que le cours est annule
		Enumeration<AgentRef> listRefInscrits = listInscrits.keys();
		
		for (; listRefInscrits.hasMoreElements();) {
			AgentRef current = listRefInscrits.nextElement();
			// send().send(new ..., current);
		}
		// on vide la liste des inscrits
		listInscrits = new Hashtable<AgentRef, Integer>();
		// maj criticite + retour comportement nominal
	}

	/*
	TODO Un pEtu veut assister a ce cours mais ne peut pas (cours plein)
	SNC2
	caseFindPlace(AgentRef a) {
		prendre le moin critique des inscrits (voir behTB)
		si il est moin critique que le pEtu qui demande une place
			notifie qu'il est desincrit du cours
			retire de la liste des inscrit
			inscrire le demandeur
			le notifie qu'il est inscrit
		sinon
			demande a son createur un nouveau cours
	}
	*/
	
	@Override
	protected Push<EDTMessage> cycle() {
		return new Push<EDTMessage>() {
			public void push(EDTMessage message) {
				pmb.match(message);
			}

		};
	}
	
	protected void debugText(String s , Object... params){
		if(debug().isDebugON()){
			debug().sendDebugMessage(new PEnsDebugText(myRef().pull(),String.format(s, params)));
		}
	}

}
